package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberRespVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    RabbitTemplate rabbitTemplate;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{
            //解决异步feign请求丢失请求头问题
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //1、远程查询所有的收货地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            orderConfirmVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //2、远程查询购物车所有选中的购物项
            List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
            orderConfirmVo.setItems(currentUserCartItems);
        }, executor).thenRunAsync(()->{
            //3、批量查询所有商品的库存
            List<OrderItemVo> items = orderConfirmVo.getItems();
            List<Long> skuIds = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            R skuHasStock = wareFeignService.getSkuHasStock(skuIds);
            List<SkuHasStockVo> data = skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
            });
            if(data!=null){
                Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                orderConfirmVo.setStocks(map);
            }
        }, executor);

        //3、查询用户积分
        Integer integration = memberRespVo.getIntegration();
        orderConfirmVo.setIntegration(integration);
        //4、其他数据自动计算

        //5、防订单重复提交令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOTOKN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
        CompletableFuture.allOf(completableFuture1,completableFuture2).get();
        return orderConfirmVo;
    }


    /**
     * @Description:  高并发下，并不推荐Seata AT模式
     *                而是采取 可靠消息 + 最终一致性
     *
     */
    //@GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        //去服务器验令牌 ,创建订单 验价格，锁库存
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        //1、验证令牌【令牌的对比和删除必须保证操作的原子性】==》 使用Lua脚本【脚本返回的是0|1 0：令牌失败  1：令牌删除成功】
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        String orderToken = vo.getOrderToken();
        //result 脚本执行的结果
        // 原子验证令牌和删除令牌
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOTOKN_PREFIX + memberRespVo.getId()), orderToken);
        /*String redisToken = redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOTOKN_PREFIX + memberRespVo.getId()); //redis中存储的token
        if(orderToken != null && orderToken.equals(redisToken)){
            //防重令牌校验通过

        }else{
            return responseVo;
        }*/
        if(result == 0){  //令牌验证失败
            responseVo.setCode(1);
            return responseVo;
        }else{
            //2、创建订单
            confirmVoThreadLocal.set(vo);
            //验价格
            OrderCreateTo order = createOrder();
            BigDecimal payAmount = order.getOrderEntity().getPayAmount();
            BigDecimal payPrice = vo.getPayPrice();
            if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
                //验价成功
                //3、保存订单
                saveOrder(order);
                //4、锁定库存 ==> 只要有异常，回滚订单数据
                //订单号，所有订单项
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(order.getOrderEntity().getOrderSn());
                List<OrderItemVo> collect = order.getItems().stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setCount(item.getSkuQuantity());
                    orderItemVo.setTitle(item.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList());
                lockVo.setLocks(collect);
                R r = wareFeignService.orderLockStock(lockVo);
                if(r.getCode()==0){
                    //锁定库存成功
                    responseVo.setOrderEntity(order.getOrderEntity());
                    responseVo.setCode(0);
                    //todo 订单创建成功后，向rabbitmq发送消息
                    System.out.println("订单创建成功，向RabbitMQ发送消息......");
                    //[注意] 交换机和路由键不要弄错
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrderEntity());
                    return responseVo;
                }else{
                    //锁定库存失败
                    throw new NoStockException(0L);
                }
            }else{
                responseVo.setCode(2);
                return responseVo;
            }
        }
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        OrderEntity order_sn = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return order_sn;
    }

    @Override
    public void closeOrder(OrderEntity entity) {
        //查询当前这个订单的最新状态
        OrderEntity byId = this.getById(entity.getId());
        // 若消息从延时队列出来后，还是未付款的状态，则取消订单
        if(byId.getStatus() == OrderStatusEnum.CREATE_NEW.getCode() ){
            OrderEntity update = new OrderEntity();
            update.setId(byId.getId());
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            //订单关闭成功，发给MQ消息，去解锁库存
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(update,orderTo);
            try {
                // todo 需要保证消息一定发送到rabbitMQ
                // 每一个发送出去的消息做好日志记录，这里是持久化到数据库
                rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other.#",orderTo);
            } catch (Exception e) {
                // todo 将没法送成功的消息进行重试发送

            }
        }
    }

    @Override
    public PayVo getPayOrder(String orderSn) {
        OrderEntity orderByOrderSn = this.getOrderByOrderSn(orderSn);
        PayVo payVo = new PayVo();
        payVo.setBody("谷粒商城收银");
        payVo.setOut_trade_no(orderSn);
        payVo.setSubject("谷粒商城收银");
        payVo.setTotal_amount(orderByOrderSn.getPayAmount().setScale(2, BigDecimal.ROUND_UP).toString());
        return payVo;
    }

    //保存订单数据（入库）
    private void saveOrder(OrderCreateTo order) {
        //1、保存订单
        OrderEntity orderEntity = order.getOrderEntity();
        orderEntity.setModifyTime(new Date());
        //orderDao.insert(orderEntity);
        this.save(orderEntity);
        //2、保存所有的订单项
        List<OrderItemEntity> items = order.getItems();
        //这里使用seata的代理数据源批量保存会保存  https://blog.csdn.net/weixin_48445640/article/details/115426087
        //orderItemService.saveBatch(items);
        for (OrderItemEntity item : items) {
            orderItemService.save(item);
        }
    }

    public OrderCreateTo createOrder(){
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        //1、生成订单
        OrderEntity orderEntity = buildOrder();
        //2、获取到所有的订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderEntity.getOrderSn());
        //3、验价
        computerPrice(orderEntity,orderItemEntities);
        orderCreateTo.setOrderEntity(orderEntity);
        orderCreateTo.setItems(orderItemEntities);
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());
        orderCreateTo.setFarePrice(orderEntity.getFreightAmount());
        return orderCreateTo;
    }

    private void computerPrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        //所有订单项的应付金额
        BigDecimal total = new BigDecimal(0);
        for (OrderItemEntity orderItemEntity : orderItemEntities) {
            total = total.add(orderItemEntity.getRealAmount());
        }
        //订单价格相关
        orderEntity.setTotalAmount(total);
        //定单应付总额 = 所有订单项的价格 + 运费价格
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        
        //设置积分信息等
    }

    private OrderEntity buildOrder() {
        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        String orderSn = IdWorker.getTimeId();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setMemberId(memberRespVo.getId());
        orderEntity.setOrderSn(orderSn);
        //获取收货地址信息 + 运费
        R fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        BigDecimal farePrice = fare.getData(new TypeReference<BigDecimal>() {
        });
        orderEntity.setFreightAmount(farePrice);
        orderEntity.setReceiverCity("宣城市");
        orderEntity.setReceiverDetailAddress("宣城市旌德县云乐镇");
        orderEntity.setReceiverName("后臣");
        orderEntity.setReceiverPhone("17775273253");
        orderEntity.setReceiverPostCode("242600");
        orderEntity.setReceiverProvince("安徽省");
        orderEntity.setReceiverRegion("");

        //设置订单的状态信息
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);
        return orderEntity;
    }


    /**
     * @Description:  根据购物项来构建所有订单项信息
     * @param
     * @author  houChen
     * @date  2022/2/11 20:45
     */
    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
        if(currentUserCartItems!=null && currentUserCartItems.size()>0){
            List<OrderItemEntity> orderItemEntities = currentUserCartItems.stream().map(cartItem -> {
                OrderItemEntity orderItemEntity = buildOrderItem(cartItem);
                orderItemEntity.setOrderSn(orderSn);
                return orderItemEntity;
            }).collect(Collectors.toList());
            return orderItemEntities;
        }
        return null;
    }

    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        //1、订单的订单号信息
        //2、商品的spu信息
        R r = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId());
        SpuInfoVo spuInfo = r.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfo.getId());
        orderItemEntity.setSpuBrand(spuInfo.getBrandId()+"");
        orderItemEntity.setSpuName(spuInfo.getSpuName());
        orderItemEntity.setCategoryId(spuInfo.getCatalogId());
        //3、商品的sku信息
        orderItemEntity.setSkuId(cartItem.getSkuId());
        orderItemEntity.setSkuName(cartItem.getTitle());
        orderItemEntity.setSkuPic(cartItem.getImg());
        orderItemEntity.setSkuPrice(cartItem.getPrice());
        String s = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ",");
        orderItemEntity.setSkuAttrsVals(s);
        orderItemEntity.setSkuQuantity(cartItem.getCount());
        //4、商品的优惠信息
        //5、购买商品赠送的积分信息
        orderItemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        orderItemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        //6、订单项的价格信息
        orderItemEntity.setPromotionAmount(new BigDecimal(0));
        orderItemEntity.setCouponAmount(new BigDecimal(0));
        orderItemEntity.setIntegrationAmount(new BigDecimal(0));
        //当前订单项的实际金额
        BigDecimal originPrice = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));
        BigDecimal realPrice = originPrice.subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(realPrice);
        return orderItemEntity;
    }
}